package frpc.manager.string;

import frpc.manager.basics.RedisBasicsBusinessManager;
import frpc.manager.lock.IRedisLockManager;
import frpc.manager.operation.GiveCacheData;
import frpc.manager.operation.RedisLockErrorFunction;
import frpc.manager.operation.RedisLockSuccessFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.ValueOperations;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 类描述:  Redis——字符串对象处理实现
 *
 * @author 王雷
 * @version 1.0.0
 * @date 2019/10/16 0016 下午 12:12
 */
@Slf4j
@SuppressWarnings("unchecked")
public class RedisSerializableManagerImpl extends RedisBasicsBusinessManager implements IRedisSerializableManager {

    /**
     * 功能描述：Redis基础处理对象
     *
     * @param iRedisLockManager Redis——锁处理接口
     * @author : 王雷
     * @date : 2020/11/26 0026 下午 4:48
     */
    protected RedisSerializableManagerImpl(IRedisLockManager iRedisLockManager) {
        super(iRedisLockManager);
    }

    /**
     * 功能描述：获取字符串处理对象完成自定义处理
     *
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> ValueOperations<String, T> giveValueOperations() {
        return (ValueOperations<String, T>) giveRedisTemplate().opsForValue();
    }

    /**
     * 功能描述：添加数据到指定的Key中，不限制时间
     *
     * @param key   需要插入的Key
     * @param value 需要插入的内容
     * @param time  失效时间
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addBatchValueByKey(String key, Map<String, T> value, long time) {
        value = (value == null) ? new HashMap<>(0) : value;
        Map<String, Object> putMap = new HashMap<>(value.size() * 2);
        final Set<String> keySet = value.keySet();
        value.forEach((k, v) -> {
            putMap.put(key + ":" + k, v);
        });
        giveValueOperations().multiSet(putMap);
        if (time > 0L) {
            keySet.clear();
            keySet.addAll(putMap.keySet());
            for (String keyValue : keySet) {
                expire(keyValue, time);
            }
        }
    }

    /**
     * 功能描述：添加数据到指定的Key中，不限制时间
     *
     * @param key   需要插入的Key
     * @param value 需要插入的内容
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addBatchValueByKey(String key, Map<String, T> value) {
        addBatchValueByKey(key, value, 0L);
    }

    /**
     * 功能描述：添加数据到指定的Key中，不限制时间
     *
     * @param key   需要插入的Key
     * @param value 需要插入的内容
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addValueByKey(String key, T value) {
        addValueByKey(key, value, 0L);
    }

    /**
     * 功能描述：添加数据到指定的Key中
     *
     * @param key   需要插入的Key
     * @param value 需要插入的内容
     * @param time  失效时间，单位：秒
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addValueByKey(String key, T value, long time) {
        if (time > 0L) {
            giveValueOperations().set(key, value, time, TimeUnit.SECONDS);
        } else {
            giveValueOperations().set(key, value);
        }
    }

    /**
     * 功能描述：添加数据到指定的Key中
     *
     * @param key   需要插入的Key
     * @param value 需要插入的内容
     * @param time  失效时间，单位：秒
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addValueByKey(String key, T value, long time, TimeUnit timeUnit) {
        if (time > 0L) {
            timeUnit = (timeUnit == null) ? TimeUnit.SECONDS : timeUnit;
            giveValueOperations().set(key, value, time, timeUnit);
        } else {
            giveValueOperations().set(key, value);
        }
    }

    /**
     * 获取数据
     *
     * @param key 键
     * @return 值
     */
    @Override
    public <T> T giveValueByKey(String key) {
        if (key == null || "".equals(key)) {
            return null;
        }
        return (T) giveValueOperations().get(key);
    }

    /**
     * 获取数据
     *
     * @param keys 键
     * @return 值
     */
    @Override
    public <T> List<T> giveBatchValueByKey(Collection<String> keys) {
        if (keys == null || keys.size() <= 0) {
            return new ArrayList<>();
        }
        return (List<T>) giveValueOperations().multiGet(keys);
    }

    /**
     * 获取数据
     *
     * @param keys 键
     * @return 值
     */
    @Override
    public <T> List<T> giveBatchValueByKey(String... keys) {
        if (keys == null || keys.length <= 0) {
            return new ArrayList<>();
        }
        return giveBatchValueByKey(Arrays.asList(keys));
    }

    /**
     * 递增
     *
     * @param key   键
     * @param delta 要增加几(大于0)
     * @return 结果
     */
    @Override
    public Long incr(String key, long delta) {
        return giveValueOperations().increment(key, Math.abs(delta));
    }

    /**
     * 递减
     *
     * @param key   键
     * @param delta 要减少几
     * @return 结果
     */
    @Override
    public Long decr(String key, long delta) {
        return giveValueOperations().increment(key, (Math.abs(delta) * -1));
    }

    private final static String LOCK_PREFIX = "Redis_Lock_";

    /**
     * 获取数据，带锁处理
     *
     * @param key           键
     * @param giveCacheData 数据无效时的回调函数
     * @return 值
     */
    @Override
    public <T> T giveValueByKeyToLock(String key, GiveCacheData<T> giveCacheData, Long putMaxTime, TimeUnit timeUnit) throws Exception {
        /*
         * 第一步：检查访问缓存对象中是否有数据
         * 访问缓存中的数据是在没有获取到值或者逻辑处理出现异常的时候才会存在；
         * 存在的时间为1分钟；
         * */
        if (Boolean.TRUE.equals(hasKey(key))) {
            return giveValueByKey(key);
        }
        return iRedisLockManager.autoLockToManager(LOCK_PREFIX + key, 0, redisTemplate -> {
            //再次验证值是否存在
            if (Boolean.TRUE.equals(hasKey(key))) {
                return giveValueByKey(key);
            }
            final T returnData = giveCacheData.giveManager();
            if (returnData != null) {
                //将数据装入缓存中，默认保存的时间为60秒
                addValueByKey(key, returnData);
                if (putMaxTime != null && putMaxTime > 0L) {
                    expire(key, putMaxTime, (timeUnit == null) ? TimeUnit.SECONDS : timeUnit);
                }
            }
            return returnData;
        });
    }

    /**
     * 获取数据，带锁处理
     *
     * @param key           键
     * @param giveCacheData 数据无效时的回调函数
     * @return 值
     */
    @Override
    public <T> T giveValueByKeyToLock(String key, GiveCacheData<T> giveCacheData) throws Exception {
        return giveValueByKeyToLock(key, giveCacheData, null, null);
    }

    /**
     * 功能描述：获取数据，带锁处理
     *
     * @param key                      需要查询的Key
     * @param redisLockSuccessFunction Redis锁处理中正常处理函数
     * @param trRedisLockErrorFunction Redis锁处理中异常处理函数
     * @param giveCacheData            需要执行的回调函数
     * @return : 结果
     * @author : 王雷
     * @date : 2020/11/30 0030 下午 7:51
     */
    @Override
    public <T, R> R giveValueByKeyToLock(String key, RedisLockSuccessFunction<T, R> redisLockSuccessFunction
            , RedisLockErrorFunction<R> trRedisLockErrorFunction, GiveCacheData<T> giveCacheData) {
        try {
            return redisLockSuccessFunction.successWorkManager(iRedisLockManager.giveRedisManage()
                    , giveValueByKeyToLock(key, giveCacheData, null, null));
        }catch(Exception e){
            log.warn("Redis——获取数据，带锁处理异常", e);
            return trRedisLockErrorFunction.errorWorkManager(iRedisLockManager.giveRedisManage(), e);
        }
    }

    /**
     * 功能描述：获取数据，带锁处理
     *
     * @param key                      需要查询的Key
     * @param redisLockSuccessFunction Redis锁处理中正常处理函数
     * @param trRedisLockErrorFunction Redis锁处理中异常处理函数
     * @param giveCacheData            需要执行的回调函数
     * @param putMaxTime               存储的时间
     * @param timeUnit                 存储时间单位
     * @return : 结果
     * @author : 王雷
     * @date : 2020/11/30 0030 下午 7:51
     */
    @Override
    public <T, R> R giveValueByKeyToLock(String key, RedisLockSuccessFunction<T, R> redisLockSuccessFunction
            , RedisLockErrorFunction<R> trRedisLockErrorFunction, GiveCacheData<T> giveCacheData, Long putMaxTime, TimeUnit timeUnit) {
        try {
            return redisLockSuccessFunction.successWorkManager(iRedisLockManager.giveRedisManage()
                    , giveValueByKeyToLock(key, giveCacheData, putMaxTime, timeUnit));
        }catch(Exception e){
            log.warn("Redis——获取数据，带锁处理异常", e);
            return trRedisLockErrorFunction.errorWorkManager(iRedisLockManager.giveRedisManage(), e);
        }
    }
}
